package com.king.designpatterns.创造型模式.工厂模式;

/**
 * 工厂方法模式
 * 工厂方法模式（Factory Method Pattern）又叫虚拟构造函数（Virtual Constructor）模式或者多态性工厂（Polymorphic Factory）模式。
 * 工厂方法模式的用意是定义一个创建产品对象的工厂接口，将实际创建性工作推迟到子类中。
 *
 * 分类：
 *      ■ 简单工厂模式：
 *          一个工厂类处于对产品类实例化的中心位置上，它知道每一个产品，它决定哪一个产品类应当被实例化。这个模式的优点是允许客户端相对独立于产品创建的过程，
 *          并且在系统引入新产品的时候无须修改客户端，即在某种程度上支持“开-闭”原则。这个模式的缺点是对“开-闭”原则的支持不够，因为如果有新的产品加入到系统中，则需要修改工厂类，
 *          将必要的逻辑加入到工厂类中。
 *      ■ 工厂方法模式：
 *          是简单工厂模式的进一步抽象和推广。由于使用了多态性，工厂方法模式保持了简单工厂模式的优点，且克服了它的缺点。首先，在工厂方法模式中，核心的工厂类不再负责所有产品的创建，
 *          而是将具体创建的工作交给子类去做。这个核心类则成为一个抽象工厂角色，仅负责给出具体工厂子类必须实现的接口，而不接触哪个产品类应当被实例化的细节。此种进一步抽象化的结果，
 *          使这种工厂方法模式可以用来允许系统在不修改具体工厂角色的情况下引进新的产品，这一特点使得工厂模式具有超过简单工厂的优越性。
 *      ■ 抽象工厂模式：
 *          是所有形态的工厂模式中最为抽象和最具有一般性的一种形态。抽象工厂模式与工厂方法模式的最大区别就在于，工厂方法模式针对的是一个产品等级结构；
 *          而抽象工厂模式则需要面对多个产品等级结构。
 *
 * 优点：
 *      ■ 良好的封装性，代码结构清晰。一个对象创建是有条件约束的，如果一个调用者需要一个具体的产品对象，只要知道这个产品的类名或约束字符串即可，不用知道创建对象的过程如何，
 *          降低了模块间的耦合。
 *      ■ 优秀的可扩展性。在增加产品类的情况下，只要适当地修改具体的工厂类或扩展一个工厂类，就可以适应变化。
 *      ■ 屏蔽产品类。产品类的实现如何变化，调用者都不需要关心，而只需要关心产品的接口，只要接口保持不变，系统的上层模块就不需要发生变化。因为产品的实例化是由工厂类负责的，
 *          具体生产何种产品对象是由不同的工厂类决定的。
 *      ■ 工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类，其他的实现类都不用关心。工厂方法模式符合迪米特法则，也符合依赖倒置原则，只依赖产品类的抽象；
 *          另外还符合里氏替换原则，可以使用产品子类替换产品父类。
 *
 * 使用场景：
 *      ■ 工厂方法模式是new一个对象的替代品，因此在所有需要生成对象的地方都可以使用，但是需要慎重考虑是否需要增加一个工厂类进行管理，增加代码的复杂度。
 *      ■ 需要灵活的、可扩展的框架时，可以考虑采用工厂方法模式。
 *      ■ 工厂方法模式可以用在异构项目中，例如，通过WebService与一个非Java的项目交互，虽然WebService号称是可以做到异构系统的同构化，但是在实际开发中，还是会碰到很多问题，
 *          如类型问题、WSDL文件的支持问题等。从WSDL中产生的对象都认为是一个产品，然后由一个具体的工厂类进行管理，减少与外围的耦合。
 *      ■ 工厂方法模式可以使用在测试驱动开发的框架下。例如，测试一个类 A，就需要将与类A关联的类B也同时产生出来，使用工厂方法模式可以将类B虚拟出来，避免类A与类B的耦合。
 *
 * @author king
 * @date 2021-06-05 21:09
 */
public class FactoryMethod {
    public static void main(String[] args) {
        // 应用代码
        Creator creator = new ConcreteCreator();
        Product product = creator.factory(ConcreteProduct.class);
        // 业务处理
    }

    /**
     * 抽象工厂
     */
    public interface Creator {
        /**
         * 工厂方法
         * 创建一个产品对象，其输入参数类型可以自行设置
         */
        public <T extends Product> T factory(Class<T> c);
    }

    /**
     * 抽象产品
     */
    public interface Product {
        // 产品类的公共方法
        public void method1();
        public void method2();
    }

    /**
     * 具体工厂
     */
    public static class ConcreteCreator implements Creator {

        @Override
        public <T extends Product> T factory(Class<T> c) {
            Product product = null;
            try {
                product = (Product)Class.forName(c.getName()).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return (T)product;
        }
    }

    /**
     * 具体产品
     */
    public class ConcreteProduct implements Product {

        @Override
        public void method1() {
            System.out.println("业务逻辑处理代码1");
        }

        @Override
        public void method2() {
            System.out.println("业务逻辑处理代码2");
        }
    }
}

